﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls.Primitives;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;

namespace TreeBuilder
{
    public class ResizeAdorner : Adorner
    {
        public Thumb TopLeft;
        public Thumb Top;
        public Thumb TopRight;
        public Thumb Right;
        public Thumb BottomRight;
        public Thumb Bottom;
        public Thumb BottomLeft;
        public Thumb Left;

        public VisualCollection Children;

        protected override int VisualChildrenCount { get { return Children.Count; } }
        protected override Visual GetVisualChild(int index) { return Children[index]; }

        private double UpperPanelHeight { get; set; }
        private NodeFrame Frame { get; set; }
        private Size MinSize { get; set; }

        private Size? InitSize { get; set; }
        private Point? InitPosition { get; set; }

        public ResizeAdorner(UIElement adornedElement, NodeFrame nodeAdorned, double upperPanelHeight, Size minSize)
            : base(adornedElement)
        {
            Frame = nodeAdorned;
            UpperPanelHeight = upperPanelHeight;
            MinSize = minSize;
            Children = new VisualCollection(this);
            BuildThumb(ref TopLeft, Cursors.ScrollNW, (obj, e) => MoveAndResize(e.HorizontalChange, e.VerticalChange, true, true));
            BuildThumb(ref Top, Cursors.ScrollNS, (obj, e) => MoveAndResize(0, e.VerticalChange, true));
            BuildThumb(ref TopRight, Cursors.ScrollNE, (obj, e) => MoveAndResize(e.HorizontalChange, e.VerticalChange, true));
            BuildThumb(ref Right, Cursors.ScrollWE, (obj, e) => MoveAndResize(e.HorizontalChange, 0));
            BuildThumb(ref BottomRight, Cursors.ScrollSE, (obj, e) => MoveAndResize(e.HorizontalChange, e.VerticalChange));
            BuildThumb(ref Bottom, Cursors.ScrollNS, (obj, e) => MoveAndResize(0, e.VerticalChange));
            BuildThumb(ref BottomLeft, Cursors.ScrollSW, (obj, e) => MoveAndResize(e.HorizontalChange, e.VerticalChange, horizontalMove:true));
            BuildThumb(ref Left, Cursors.ScrollWE, (obj, e) => MoveAndResize(e.HorizontalChange, 0, horizontalMove: true));

            foreach (var thumb in Children.OfType<Thumb>())
            {
                thumb.DragStarted += (obj, args) => OnDragStart();
                thumb.DragCompleted += (obj, args) => OnDragCompleted(args);
            }

        }

        public event Action<UndoableAction> MoveAndResizeComplete;

        protected virtual void OnMoveAndResizeComplete(UndoableAction obj)
        {
            Action<UndoableAction> handler = MoveAndResizeComplete;
            if (handler != null) handler(obj);
        }

        public void MoveAndResize(double xDelta, double yDelta, bool verticalMove = false, bool horizontalMove = false)
        {
            var resizeXDelta = horizontalMove ? -xDelta : xDelta;
            var resizeYDelta = verticalMove ? -yDelta : yDelta;
            double width = Math.Max(MinSize.Width, Frame.Size.Width + resizeXDelta);
            double height = Math.Max(MinSize.Height, Frame.Size.Height + resizeYDelta);

            Frame.Size = new Size(width, height);

            double newX = Frame.TopCenter.X + xDelta / 2;
            double newY = verticalMove ? Frame.TopCenter.Y + yDelta : Frame.TopCenter.Y;

            Frame.TopCenter = new Point(newX, newY);
        }


        private void OnDragCompleted(DragCompletedEventArgs e)
        {
            if (e.Canceled)
            {
                Frame.Size = InitSize.Value;
                Frame.TopCenter = InitPosition.Value;

                InitPosition = null;
                InitSize = null;
                return;
            }

            var moveAction = new MoveNodeAction(Frame, Frame.TopCenter, InitPosition.Value);
            var resizeAction = new SizeChangedAction(Frame, InitSize.Value, Frame.Size);
            var action = new CombinedAction(moveAction, resizeAction);

            OnMoveAndResizeComplete(action);
        }

        private void OnDragStart()
        {
            InitSize = Frame.Size;
            InitPosition = Frame.TopCenter;
        }


        void BuildThumb(ref Thumb thumb, Cursor customizedCursor, DragDeltaEventHandler action)
        {
            if (thumb != null) return;

            thumb = new Thumb();

            // Set some arbitrary visual characteristics.
            thumb.Cursor = customizedCursor;
            thumb.Height = thumb.Width = 10;
            thumb.Opacity = 0.40;
            thumb.Background = new SolidColorBrush(Colors.MediumBlue);
            thumb.DragDelta += action;

            Children.Add(thumb);
        }

        public bool IsActive
        {
            get { return (bool)GetValue(IsActiveProperty); }
            set { SetValue(IsActiveProperty, value); }
        }

        // Using a DependencyProperty as the backing store for IsActive.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty IsActiveProperty =
            DependencyProperty.Register("IsActive", typeof(bool), typeof(ResizeAdorner), new PropertyMetadata(false));

        protected override Size ArrangeOverride(Size finalSize)
        {
            double desiredWidth = AdornedElement.DesiredSize.Width;
            double desiredHeight = AdornedElement.DesiredSize.Height;
            // adornerWidth & adornerHeight are used for placement as well.
            double adornerWidth = this.DesiredSize.Width;
            double adornerHeight = this.DesiredSize.Height;

            TopLeft.Arrange(new Rect(-adornerWidth / 2, -adornerHeight / 2, adornerWidth, adornerHeight));
            Top.Arrange(new Rect(desiredWidth/2 - adornerWidth / 2, -adornerHeight / 2, adornerWidth, adornerHeight));
            TopRight.Arrange(new Rect(desiredWidth - adornerWidth / 2, -adornerHeight / 2, adornerWidth, adornerHeight));
            Right.Arrange(new Rect(desiredWidth - adornerWidth / 2, desiredHeight/2 - adornerHeight / 2, adornerWidth, adornerHeight));
            BottomLeft.Arrange(new Rect(-adornerWidth / 2, desiredHeight - adornerHeight / 2, adornerWidth, adornerHeight));
            Bottom.Arrange(new Rect(desiredWidth / 2 - adornerWidth / 2, desiredHeight - adornerHeight / 2, adornerWidth, adornerHeight));
            BottomRight.Arrange(new Rect(desiredWidth - adornerWidth / 2, desiredHeight - adornerHeight / 2, adornerWidth, adornerHeight));
            Left.Arrange(new Rect(-adornerWidth / 2, desiredHeight/2 - adornerHeight / 2, adornerWidth, adornerHeight));

            return finalSize;
        }
    }
}
